<script setup>
import { ref, h } from 'vue';
import {
  MapBox,
  MapDraw,
  MapUtils,
  PolygonLayer,
  PolylineLayer,
  PopupOverlay,
  SymbolLayer
} from '@/libs/MapBox/dist/index.js';
import { message } from 'ant-design-vue';
import * as turf from '@turf/turf';
import IconSvg from '@/components/icon/iconSvg.vue';
import { DXF_LAYERS, TYPES } from '@/components/roomConstant/common.js';
import { FormOutlined } from '@ant-design/icons-vue';

const props = defineProps({
  isEdit: {
    type: Boolean,
    default: false
  },
  mapExpression: {
    type: Array,
    default: () => ([])
  },
  isAssign: {
    type: Boolean,
    default: false
  }
});

const mapContainerRef = ref();
const map = ref();
const mapDraw = ref();
const layers = reactive({});
const isEditing = ref(false);
const isAssigning = ref(false);
const dealData = ref(
  {
    deleteRoomIds: [],
    saveRooms: []
  }
)

// 图层类型配置
const layersTypeOptions = {
  bottomLineLayer: {
    class: PolylineLayer,
    options: {}
  },
  polygonLayer: {
    class: PolygonLayer,
    options: {}
  },
  polygonLineLayer: {
    class: PolylineLayer,
    options: {}
  },
  polygonTextLayer: {
    class: SymbolLayer,
    options: {}
  }
};

// 绘制面
const drawOperation = reactive({
  cacheDrawFeature: null,
  cacheFeatureMap: new Map(),
  cacheNameMap: new Map(),
});

const fatherData = inject('fatherData');

// 特殊房间表达式
const specialRoomExpression = ref();

// 初始化地图
const initMap = () => {
  if (map.value) return;
  map.value = new MapBox(mapContainerRef.value, {})
    .onLoad(() => {
      initBottomLayer();
      initEvents();
      initDraw();
      setDataAndStyle();
      getPolygonCenter();
      // initMarkerPopup();
    });
};

// 初始化底图
const initBottomLayer = () => {
  Object.keys(layersTypeOptions).forEach((key) => {
    layers[key] = new layersTypeOptions[key].class(map.value.instance, {
      id: key,
      ...layersTypeOptions[key].options,
    });
  });
  // console.log('aly== ', layers);
};

// 初始化事件
const initEvents = () => {
  if (!layers) {
    initBottomLayer();
  }
  const polygonLayer = layers[TYPES.POLYGON];
  polygonLayer.on('mousemove', (e) => {
    if (isEditing.value) return;
    const [feature] = MapUtils.getEventFeatures(e, map.value.instances);
    if (!feature) return;
    const expression = specialRoomExpression.value ?
      specialRoomExpression.value :
      ['case', ['has', 'color'], ['get', 'color'], 'rgba(59, 178, 208, 0.6)'];

    polygonLayer.options.paint['fill-color'] = [
      'case',
      ['==', ['get', 'id'], feature.properties.id],
      'rgba(255,244,61,0.8)',
      expression,
    ];
  });
  polygonLayer.on('mouseleave', () => {
    if (isEditing.value) return;
    polygonLayer.options.paint['fill-color'] = specialRoomExpression.value ?
      specialRoomExpression.value :
      ['case', ['has', 'color'], ['get', 'color'], 'rgba(59, 178, 208, 0.6)'];
  });
  polygonLayer.on('click', (e) => {
    if (isEditing.value) return;
    const [feature] = MapUtils.getEventFeatures(e, map.value.instances);
    if (!feature) return;
    if (isAssigning.value) {
      if (feature.properties.hasOwnProperty('currentPeople')) {
        fatherData.assignFeature = feature;
      } else {
        feature.properties.layer === DXF_LAYERS.TX ?
          message.info('请先给房间分配属性') :
          message.info('该房间为公共区域');
      }
    } else {
      fatherData.roomInfoFeature = {
        isDraw: false,
        feature
      };
    }
  });
};

// 初始化绘制
const initDraw = () => {
  mapDraw.value = new MapDraw(map.value.instance)
    .onCreate(drawing)
    .onUpdate(drawing)
    .onDelete(drawing)
    .onSplit(drawing)
    .onMerge(drawing);
};

// 获取偏移坐标
const getOffsetCoords = (polygon, offsetCenter = 0) => {
  // 获取包围盒
  const bbox = turf.bbox(polygon);
  const [minX, minY, maxX, maxY] = bbox;
  // 计算中心点
  const center = turf.center(polygon);
  const [x, y] = center.geometry.coordinates;

  // 计算安全的偏移距离（不超过多边形高度的一半）
  const polygonHeight = maxY - minY;
  const safeOffset = polygonHeight * offsetCenter; // 偏移30%的高度距离

  // 向上偏移但确保不超过多边形上边界
  const targetY = Math.min(y + safeOffset, maxY - (polygonHeight * 0.1)); // 留10%边距
  return [x, targetY];
};

const createPoint = (polygon, valArr, addPid = true) => {
  const points = [];
  valArr.forEach((item) => {
    const point = turf.point(getOffsetCoords(polygon, item?.offsetCenter), {
      id: MapUtils.randomString(),
      name: item.name,
    });
    point.id = point.properties.id;
    addPid && (point.properties.pid = polygon.properties.id);
    points.push(point);
  });
  return points;
};

const checkLayers = (features) => {
  let layers = {
    [DXF_LAYERS.TX]: 0,
    [DXF_LAYERS.GG]: 0,
  };
  let layer = DXF_LAYERS.GG;
  features.forEach(item => {
    const area = turf.area(item);
    if (item.properties.layer === 'TX') {
      layers.TX += area;
    } else if (item.properties.layer === 'GG') {
      layers.GG += area;
    }
  });
  const max = Math.max(...Object.values(layers));
  Object.keys(layers).forEach(key => {
    if (layers[key] === max) {
      layer = key;
    }
  });
  return layer;
};

const splitAndMerge = (e) => {
  const textMap = new Map();
  let layer = 'GG';
  switch (e.type) {
    case 'draw.split':
      let areaTotal = 0;
      e.deletedFeatures.forEach(item => {
        if (item.properties.oId && dealData.value.deleteRoomIds.indexOf(item.properties.oId) === -1) {
          dealData.value.deleteRoomIds.push(item.properties.oId);
        }
        dealData.value.saveRooms = dealData.value.saveRooms.filter(item0 => item0.id !== item.id)

        areaTotal += parseFloat(item.properties.area || 0);
      });
      layer = checkLayers(e.deletedFeatures);

      let total = 0;
      e.createdFeatures.forEach(item => {
        const turfArea = turf.area(item);
        total += turfArea;
        textMap.set(item.properties.id, turfArea);
      });
      textMap.forEach((item, key) => {
        const ratio = item / total;
        const area = areaTotal * ratio;
        textMap.set(key, area);
      });
      e.createdFeatures.forEach(item => {
        const area = textMap.get(item.properties.id);
        item.properties.color = fatherData.layerColorOptions[layer];
        item.properties.layer = layer;
        item.properties.area = area.toFixed(2);
        mapDraw.value.add(item);
        dealData.value.saveRooms.push({
          id: item.id,
          oId: item.properties.oId,
          pois: JSON.stringify(item.geometry)
        });
      });
      break;
    case 'draw.merge':
      let area = 0;
      e.deletedFeatures.forEach((item) => {
        if (item.properties.oId && dealData.value.deleteRoomIds.indexOf(item.properties.oId) === -1) {
          dealData.value.deleteRoomIds.push(item.properties.oId);
        }
        dealData.value.saveRooms = dealData.value.saveRooms.filter(item0 => item0.id !== item.id)
        area += parseFloat(item.properties.area || 0);
      });
      layer = checkLayers(e.deletedFeatures);
      let item = e.createdFeatures[0];
      console.log(item)
      e.createdFeatures[0].properties.color = fatherData.layerColorOptions[layer];
      e.createdFeatures[0].properties.layer = layer;
      e.createdFeatures[0].properties.area = area.toFixed(2);
      mapDraw.value.add(item);
      dealData.value.saveRooms.push({
        id: item.id,
        oId: item.properties.oId,
        pois: JSON.stringify(item.geometry)
      });
      console.log(dealData.value)
      break;
  }
};

const drawing = (e) => {
  switch (e.type) {
    case 'draw.split':
    case 'draw.merge':
      splitAndMerge(e);
      break;
    case 'draw.update':
      e.features.forEach(item => {
        const findName = drawOperation.cacheNameMap.get(item.properties.id);
        if (findName) {
          findName.geometry.coordinates = getOffsetCoords(item, 0.3);
          mapDraw.value.add(findName);
        }
        dealData.value.saveRooms = dealData.value.saveRooms.filter(item0 => item0.id !== item.id)
        dealData.value.saveRooms.push({
          id: item.id,
          oId: item.properties.oId,
          pois: JSON.stringify(item.geometry)
        });
      });
      break;
    case 'draw.create':
      e.features.forEach((item) => {
        dealData.value.saveRooms.push({
          id: item.id,
          oId: item.properties.oId,
          pois: JSON.stringify(item.geometry)
        });
      });
      break;
  }
  drawOperation.cacheDrawFeature = mapDraw.value.getAll();
  map.value.instance.getSource(TYPES.POLYGON_TEXT).setData(drawOperation.cacheDrawFeature);
};

const changeMode = (mode) => {
  const selected = mapDraw.value.getSelected();
  switch (mode) {
    case 'draw_split':
      if (selected.features.length === 0) {
        message.warning('请选择一个面');
        return;
      }
      break;
    case 'draw_merge':
      if (selected.features.length < 2) {
        message.warning('请选择二个面进行合并');
        return;
      }
      break;
    case 'frame_select':
      if (selected.features.length === 0) {
        message.warning('点击地图进行框选');
      }
      break;
  }
  mapDraw.value.changeMode(mode);
};

const onEditProperty = () => {
  const selected = mapDraw.value.getSelected();
  if (selected.features.length === 0 || selected.features.length > 1) {
    message.warning('请选择一个面');
    return;
  }
  if (selected.features[0].geometry.type !== 'Polygon') {
    message.warning('请选择面');
    return;
  }
  fatherData.roomInfoFeature = {
    isDraw: true,
    feature: selected.features[0],
    featuresList: drawOperation.cacheDrawFeature?.features,
  };
};

const onDelete = () => {
  const selected = mapDraw.value.getSelected();
  if (selected.features.length === 0) {
    message.warning('请选择一个面');
    return;
  }
  // console.log(selected)
  // deleteRooms(selected.features.map(item => item.id));
  mapDraw.value.trash();
  selected.features.forEach(item => {
    const findFeature = drawOperation.cacheNameMap.get(item.properties.id);
    findFeature && mapDraw.value.delete([findFeature.properties.id]);
    if (item.properties.oId && dealData.value.deleteRoomIds.indexOf(item.properties.oId) === -1) {
      dealData.value.deleteRoomIds.push(item.properties.oId);
    }
    dealData.value.saveRooms = dealData.value.saveRooms.filter(item0 => item0.id !== item.id)
  });
};

const setDataAndStyle = (layerArr = Object.keys(layersTypeOptions)) => {
  if (!layers) {
    initBottomLayer();
  }
  if (!fatherData.currentRowData) return;
  layerArr.forEach((key) => {
    switch (key) {
      case TYPES.BOTTOM_LINE:
        layers[key].options.paint = {
          'line-color': 'rgba(0,0,0,0.6)',
          'line-width': 2,
        };
        layers[key].options.source.data = fatherData.currentRowData.data;
        break;
      case TYPES.POLYGON:
        layers[key].options.paint = {
          'fill-color': ['case', ['has', 'color'], ['get', 'color'], 'rgba(59, 178, 208, 0.3)'],
        };
        layers[key].options.source.data = fatherData.currentRowData.data;
        map.value.instance.getSource(TYPES.POLYGON).setData(fatherData.currentRowData.data);
        break;
      case TYPES.POLYGON_LINE:
        layers[key].options.paint = {
          'line-color': ['case', ['has', 'color'], ['get', 'color'], 'rgba(36,110,246,0.3)'],
          'line-width': 2,
        };
        layers[key].options.source.data = fatherData.currentRowData.data;
        map.value.instance.getSource(TYPES.POLYGON_LINE).setData(fatherData.currentRowData.data);
        break;
      case TYPES.POLYGON_TEXT:
        layers[key].options.layout = {
          'text-field': '{name}\n{area}',
          'text-size': 12,
          'text-anchor': 'center',
          'text-justify': 'center',
        };
        layers[key].options.source.data = fatherData.currentRowData.data;
        map.value.instance.getSource(TYPES.POLYGON_TEXT).setData(fatherData.currentRowData.data);
        break;
    }
  });
};

const getPolygonCenter = () => {
  if (!fatherData.currentRowData) return;
  if (!fatherData.currentRowData.properties?.center) return;
  map.value.instance.jumpTo({
    center: fatherData.currentRowData.properties.center,
    zoom: fatherData.currentRowData.properties.zoom,
  });
};

const hidePolygonLayer = () => {
  layers[TYPES.POLYGON].options.paint['fill-color'] = 'transparent';
};

watch(() => props.isEdit, (newValue) => {
  isEditing.value = newValue;
});

watch(() => props.isAssign, (newValue) => {
  isAssigning.value = newValue;
});

watch(() => fatherData.currentRowData, (newValue) => {
  if (!newValue) return;
  dealData.value.saveRooms = [];
  dealData.value.deleteRoomIds = [];
  setDataAndStyle();
  getPolygonCenter();
  // initMarkerPopup();
});

watch(() => props.mapExpression, (newValue) => {
  specialRoomExpression.value = newValue;
});

// 绘制要素
watch(() => drawOperation.cacheDrawFeature, (newValue) => {
  // console.log('cacheDrawFeature= ', newValue);
  if (!newValue || !newValue.features.length) {
    drawOperation.cacheFeatureMap.clear();
    drawOperation.cacheNameMap.clear();
  } else {
    newValue.features.forEach(feature => {
      drawOperation.cacheFeatureMap.set(feature.properties.id, feature);

      if (feature.geometry.type === 'Point' &&
        !feature.properties.name?.includes('.')) {
        drawOperation.cacheNameMap.set(feature.properties.pid, feature);
      }
    });
  }
}, { deep: true });

onMounted(() => {
  initMap();
});

onBeforeUnmount(() => {
  mapDraw.value && mapDraw.value.destroy();
  map.value && map.value.destroy();
  map.value = mapDraw.value = null;
});

defineExpose({
  mapDraw,
  dealData,
  layers,
  map,
  drawOperation,
  hidePolygonLayer,
  setDataAndStyle,
  getPolygonCenter,
  createPoint,
  getOffsetCoords,
});
</script>

<template>
  <div class="map-container">
    <div class="map" ref="mapContainerRef"></div>
    <div v-show="isEditing" class="operation-btn">
      <a-tooltip title="矩形" placement="left">
        <a-button type="primary" shape="circle" @click="changeMode('draw_rectangle')">
          <icon-svg type="icon-rectangle" />
        </a-button>
      </a-tooltip>
      <a-tooltip title="多边形" placement="left">
        <a-button type="primary" shape="circle" @click="changeMode('draw_polygon')">
          <icon-svg type="icon-polygon" />
        </a-button>
      </a-tooltip>
      <a-tooltip title="框选" placement="left">
        <a-button style="background: #46ac6b" type="primary" shape="circle" @click="changeMode('frame_select')">
          <icon-svg type="icon-frameSelect" />
        </a-button>
      </a-tooltip>
      <a-tooltip title="切分" placement="left">
        <a-button type="primary" shape="circle" @click="changeMode('draw_split')">
          <icon-svg type="icon-segmentation" />
        </a-button>
      </a-tooltip>
      <a-tooltip title="合并" placement="left">
        <a-button type="primary" shape="circle" @click="changeMode('draw_merge')">
          <icon-svg type="icon-merge" />
        </a-button>
      </a-tooltip>
      <a-tooltip title="属性编辑" placement="left">
        <a-button style="background: orange" type="primary" shape="circle" @click="onEditProperty"
          :icon="h(FormOutlined)">
        </a-button>
      </a-tooltip>
      <a-tooltip title="删除" placement="left">
        <a-button danger type="primary" shape="circle" @click="onDelete">
          <icon-svg type="icon-delete" />
        </a-button>
      </a-tooltip>
    </div>
  </div>
</template>

<style scoped>
.map-container {
  width: 100%;
  height: 100%;
  position: relative;

  .map {
    width: 100%;
    height: 100%;
    z-index: 1
  }


  :deep(.mapboxgl-popup-tip) {
    position: relative;
    height: 15px;
    border: 1px solid #707070;
    background: #707070;

    &::before {
      content: '';
      position: absolute;
      bottom: -5px;
      /* 圆的底部与尖端底部的距离 */
      left: 50%;
      /* 圆的水平位置 */
      width: 8px;
      /* 圆的直径 */
      height: 8px;
      /* 圆的直径 */
      background-color: #707070;
      /* 圆的颜色 */
      border-radius: 50%;
      /* 使元素呈圆形 */
      transform: translateX(-50%);
      /* 将圆水平居中 */
      z-index: -1;
      /* 确保圆在弹出窗口尖端下方 */
    }
  }

  :deep(.mapboxgl-popup-content) {
    padding: 5px 10px;
    background: #e5f3ff;
    border: 2px solid #707070;
    border-radius: 5px;
    color: #606266;
    font-weight: bold;
    text-align: center;

    &>div {
      transition: all 0.3s linear;
    }
  }

  .operation-btn {
    position: absolute;
    z-index: 1;
    right: 5px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    gap: 15px;

    .ant-btn {
      &:hover {
        filter: opacity(0.8);
      }
    }
  }

}
</style>
